package com.zhao.util;

import com.google.common.base.CaseFormat;
import com.zhao.constant.Constant;
import com.zhao.constant.DataSource;
import com.zhao.constant.JDBCTypeConstant;
import com.zhao.dto.Column;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 生成工具
 * @Author:zhao
 * @Date: 2018/8/26 09:32
 * @Description:
 */
@Slf4j
@SuppressWarnings("all")
public class GenerationUtil {

    public static void main(String[] args){
        GenerationUtil.generateEntitys("BODY","USER");
    }

    public static void generateEntitys(String... table){
        if (table==null||table.length==0){
            return;
        }
        for(String item:table){
            List<Column> list=generateEntity(item);
            writeEntity(item,list);
            writeDao(item,list);
        }
    }

    /**
     * 生成指定表的列数据实体
     * @param table
     * @return
     */
    private static List<Column> generateEntity(String table){
        log.info("实体类[{}]生成.",table);
        List<Column> list=new ArrayList<>();

        Connection connection=null;
        DatabaseMetaData databaseMetaData=null;
        ResultSet resultSet=null;
        ResultSet primaryKeyResultSet=null;
        try{
            connection=getConnection();
            databaseMetaData=connection.getMetaData();
            resultSet=databaseMetaData.getColumns(null,getSchema(connection),table,"%");
            while (resultSet.next()){
                String name=resultSet.getString("COLUMN_NAME");
                String remark=resultSet.getString("REMARKS");
                String dbType=resultSet.getString("TYPE_NAME");
                list.add(new Column(getLowerName(name),name,remark,dbType, JDBCTypeConstant.getJavaType(dbType),false));
            }
            primaryKeyResultSet=databaseMetaData.getPrimaryKeys(null,getSchema(connection),table);
            while (primaryKeyResultSet.next()){
                String columnName = primaryKeyResultSet.getString("COLUMN_NAME");
                for (Column column:list){
                    if (column.getDbName().equals(columnName)){
                        column.setPrimaryKey(true);
                    }
                }
            }
        }catch (Exception e){
            log.error("实体类[{}]生成失败，异常信息:[{}]",table,e);
        }finally {
            try{
                primaryKeyResultSet.close();
                resultSet.close();
                connection.close();
            }catch (Exception e){
                log.error("[{}]数据库连接关闭失败.",table);
            }
        }
        return list;
    }

    /**
     * 写文件
     * @param table
     * @param columns
     */
    private static void writeEntity(String table,List<Column> columns){
        String className=getUpperName(table);
        StringBuffer buffer=new StringBuffer();

        buffer.append("package ").append(Constant.PACKAGE_ENTITY).append(";");
        buffer.append("\n");
        for (String str:getJavaTypeFull(columns)){
            if (StringUtils.isBlank(str)){
                continue;
            }
            buffer.append("\n").append("import ").append(str).append(";");
        }
        buffer.append("\n").append("import lombok.ToString;");
        buffer.append("\n").append("import lombok.Data;");
        buffer.append("\n\n").append("@Data");
        buffer.append("\n").append("@ToString");
        buffer.append("\n").append("public class ").append(className).append("{");
        for(Column column:columns){
            buffer.append("\n\n\t").append("/*").append(column.getRemark()).append("*/");
            buffer.append("\n\t").append("private ").append(column.getValueType()).append(" ").append(column.getName()).append(";");
        }
        buffer.append("\n\n").append("}");
        FileUtil.write(Constant.PATH_ENTITY+ File.separator+className+".java",buffer.toString());
    }

    /**
     * 写文件
     * @param table
     * @param columns
     */
    private static void writeDao(String table,List<Column> columns){
        String className=getUpperName(table)+"Dao";
        StringBuffer buffer=new StringBuffer();

        buffer.append("package ").append(Constant.PACKAGE_DAO).append(";");
        buffer.append("\n\n").append("import ").append(Constant.PACKAGE_ENTITY).append(".").append(getUpperName(table)).append(";");

        buffer.append("\n").append("import org.apache.ibatis.annotations.*;");
        buffer.append("\n").append("import org.apache.ibatis.jdbc.SQL;");
        buffer.append("\n").append("import org.springframework.stereotype.Component;");

        buffer.append("\n\n").append("@Mapper");
        buffer.append("\n").append("@Component");
        buffer.append("\n").append("public interface ").append(className).append("{");

        List<String> daoStrList=new ArrayList<>();
        List<String> sqlStrList=new ArrayList<>();

        //insert
        insert(table,columns,daoStrList,sqlStrList);

        //update
        update(table,columns,daoStrList,sqlStrList);

        //delete
        delete(table,columns,daoStrList,sqlStrList);

        if (daoStrList!=null&&!daoStrList.isEmpty()){
            for(String str:daoStrList){
                buffer.append(str);
            }
        }
        buffer.append("\n\n\t").append("class SqlProvider{");
        if (sqlStrList!=null&&!sqlStrList.isEmpty()){
            for (String str:sqlStrList){
                buffer.append(str);
            }
        }
        buffer.append("\n\t").append("}");
        buffer.append("\n\n").append("}");
        FileUtil.write(Constant.PATH_DAO+ File.separator+className+".java",buffer.toString());
    }

    /**
     * 插入SQL
     * @param daoStrList
     * @param sqlStrList
     */
    private static void insert(String table,List<Column> columns,List<String> daoStrList,List<String> sqlStrList){
        String methodName="insert";
        String className=getUpperName(table)+"Dao";

        StringBuffer daoStrBuffer=new StringBuffer();
        daoStrBuffer.append("\n\n\t").append("//插入");
        daoStrBuffer.append("\n\t").append("@InsertProvider(type = ").append(className).append(".SqlProvider.class,method = \"").append(methodName).append("\")");
        daoStrBuffer.append("\n\t").append("Integer ").append(methodName).append("(").append(getUpperName(table)).append(" record);");
        daoStrList.add(daoStrBuffer.toString());

        StringBuffer sqlStrBuffer=new StringBuffer();
        sqlStrBuffer.append("\n\n\t\t").append("public static String ").append(methodName).append("(final ").append(getUpperName(table)).append(" record){");
        sqlStrBuffer.append("\n\t\t\t").append("return new SQL(){{").append("\n\t\t\t\t");
        sqlStrBuffer.append("INSERT_INTO(\"").append(table).append("\");");
        for(Column column:columns){
            sqlStrBuffer.append("\n\t\t\t\t").append("VALUES(\"").append(column.getDbName()).append("\", \"#{").append(column.getName()).append("}\");");
        }
        sqlStrBuffer.append("\n\t\t\t").append("}}.toString();");
        sqlStrBuffer.append("\n\t\t").append("}");
        sqlStrList.add(sqlStrBuffer.toString());
    }

    /**
     * 更新SQL
     * @param daoStrList
     * @param sqlStrList
     */
    private static void update(String table,List<Column> columns,List<String> daoStrList,List<String> sqlStrList){
        String methodName="update";
        String className=getUpperName(table)+"Dao";

        StringBuffer daoStrBuffer=new StringBuffer();
        daoStrBuffer.append("\n\n\t").append("//更新");
        daoStrBuffer.append("\n\t").append("@UpdateProvider(type = ").append(className).append(".SqlProvider.class,method = \"").append(methodName).append("\")");
        daoStrBuffer.append("\n\t").append("Integer ").append(methodName).append("(").append(getUpperName(table)).append(" record);");
        daoStrList.add(daoStrBuffer.toString());

        StringBuffer sqlStrBuffer=new StringBuffer();
        sqlStrBuffer.append("\n\n\t\t").append("public static String ").append(methodName).append("(final ").append(getUpperName(table)).append(" record){");
        sqlStrBuffer.append("\n\t\t\t").append("return new SQL(){{");
        sqlStrBuffer.append("\n\t\t\t\t").append("UPDATE(\"").append(table).append("\");");
        for (Column column:columns){
            if (column.isPrimaryKey()){
                continue;
            }
            sqlStrBuffer.append("\n\t\t\t\t").append("if(record.get").append(getUpperFirstLetter(column.getName())).append("()!=null){");
            sqlStrBuffer.append("\n\t\t\t\t\t").append("SET(\"").append(column.getDbName()).append("=#{").append(column.getName()).append("}\");");
            sqlStrBuffer.append("\n\t\t\t\t").append("}");
        }
        sqlStrBuffer.append("\n\t\t\t\t").append("WHERE(");
        for (Column column:columns){
            if (!column.isPrimaryKey()){
                continue;
            }
            sqlStrBuffer.append("\"").append(column.getDbName()).append("=#{").append(column.getName()).append("}\",");
        }
        sqlStrBuffer.deleteCharAt(sqlStrBuffer.length() - 1);
        sqlStrBuffer.append(");");
        sqlStrBuffer.append("\n\t\t\t").append("}}.toString();");
        sqlStrBuffer.append("\n\t\t").append("}");
        sqlStrList.add(sqlStrBuffer.toString());
    }

    /**
     * 删除SQL
     * @param daoStrList
     * @param sqlStrList
     */
    private static void delete(String table,List<Column> columns,List<String> daoStrList,List<String> sqlStrList){
        String methodName="delete";
        String className=getUpperName(table)+"Dao";

        StringBuffer daoStrBuffer=new StringBuffer();
        daoStrBuffer.append("\n\n\t").append("//删除");
        daoStrBuffer.append("\n\t").append("@DeleteProvider(type = ").append(className).append(".SqlProvider.class,method = \"").append(methodName).append("\")");
        daoStrBuffer.append("\n\t").append("Integer ").append(methodName).append("(").append(getUpperName(table)).append(" record);");
        daoStrList.add(daoStrBuffer.toString());

        StringBuffer sqlStrBuffer=new StringBuffer();
        sqlStrBuffer.append("\n\n\t\t").append("public static String ").append(methodName).append("(final ").append(getUpperName(table)).append(" record){");
        sqlStrBuffer.append("\n\t\t\t").append("return new SQL(){{");
        sqlStrBuffer.append("\n\t\t\t\t").append("DELETE_FROM(\"").append(table).append("\");");
        sqlStrBuffer.append("\n\t\t\t\t").append("WHERE(");
        for (Column column:columns){
            if (!column.isPrimaryKey()){
                continue;
            }
            sqlStrBuffer.append("\"").append(column.getDbName()).append("=#{").append(column.getName()).append("}\",");
        }
        sqlStrBuffer.deleteCharAt(sqlStrBuffer.length() - 1);
        sqlStrBuffer.append(");");
        sqlStrBuffer.append("\n\t\t\t").append("}}.toString();");
        sqlStrBuffer.append("\n\t\t").append("}");
        sqlStrList.add(sqlStrBuffer.toString());
    }

    /**
     * 去重
     * @param columns
     * @return
     */
    private static List<String> getJavaTypeFull(List<Column> columns){
        List<String> list=new ArrayList<>();
        for (Column column:columns){
            list.add(JDBCTypeConstant.getJavaTypeFull(column.getDbType()));
        }
        return list.stream().distinct().collect(Collectors.toList());
    }

    /**
     * test_data->testData
     * @param name
     * @return
     */
    private static String getLowerName(String name){
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name);
    }

    /**
     * test_data->TestData
     * @param name
     * @return
     */
    private static String getUpperName(String name){
        return  CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name);
    }

    /**
     * 首字母大写
     * @param name
     * @return
     */
    private static String getUpperFirstLetter(String name){
        if(Character.isUpperCase(name.charAt(0)))
            return name;
        else
            return (new StringBuilder()).append(Character.toUpperCase(name.charAt(0))).append(name.substring(1)).toString();
    }

    /**
     * 获取数据库链接
     * @return
     * @throws Exception
     */
    private static Connection getConnection() throws Exception{
        //1.加载驱动程序
        Class.forName(DataSource.DRIVER);
        //2.获得数据库链接
        Connection connection= DriverManager.getConnection(DataSource.URL, DataSource.USER, DataSource.PASSWORD);
        return connection;
    }

    /**
     * 获取用户方案名称
     * @param connection
     * @return
     * @throws Exception
     */
    private static String getSchema(Connection connection) throws Exception{
        if (Constant.DRIVER_NAME_MYSQL.equals(connection.getMetaData().getDriverName())){
            return null;
        }
        String schema=connection.getMetaData().getUserName();
        if(StringUtils.isBlank(schema)){
            throw new Exception("ORACLE模式不允许为空");
        }
        return schema;
    }
}
